package sess

import (
	"time"

	"gitee.com/night-tc/gobige/common"
	"gitee.com/night-tc/gobige/global"
	"gitee.com/night-tc/gobige/logger"
	"gitee.com/night-tc/gobige/msgdef"
	"gitee.com/night-tc/gobige/msgdef/protomsg"
	"gitee.com/night-tc/gobige/msghandler"
	"gitee.com/night-tc/gobige/pool"
)

type INetSess interface {
	/* 外部接口 */

	//发消息
	Send(msg msgdef.IMsg)
	//发消息二进制数据
	SendBuf(data []byte)
	//立即发送
	SendNow(msg msgdef.IMsg)
	//开始
	Start()
	//关闭 只会被运行一次
	Close()
	// 延时关闭连接
	DelayClose()

	/* 属性 */

	//设置消息路由
	//一开始使用的都是service的，验证过后
	//如果是用户就换成用户的
	//剩下的还是使用service的
	SetMsgHandler(msgh msghandler.IMsgHandler)
	//设置表示连接的ID，一般与上层的服务器ID或是用户ID一致
	SetID(uint64)
	//表示连接的ID，一般与上层的服务器ID或是用户ID一致
	GetID() uint64
	//如果连接是服务器过来的话，那就是服务器类型，2表示客户端
	GetType() global.ServerTypeEnum
	//设置如果连接是服务器过来的话，那就是服务器类型，2表示客户端
	SetType(stype global.ServerTypeEnum)
	//远程地址
	RemoteAddr() string
	//是否验证过
	IsVerified() bool
	//设置验证过
	SetVerify()
}

////////////////////////////////////////////////////////////////
/* 外部接口 */

// 发消息
func (this *NetSess) Send(msg msgdef.IMsg) {
	this.sendBuf.Put(msg)
}

// 发消息二进制数据
func (this *NetSess) SendBuf(data []byte) {
	this.sendBuf.Put(data)
}

// 立即发送
func (this *NetSess) SendNow(msg msgdef.IMsg) {
	if this.isClose.Load() {
		logger.Warnf("[NetSess] Send after sess close %s %s", this.conn.RemoteAddr(), msg)
		return
	}
	buf := pool.Get(MaxMsgBuffer)
	defer pool.Put(buf)
	msgBuf, err := EncodeMsgWithEncrypt(msg, buf, true, this.encryEnabled)
	if err != nil {
		logger.Error("[NetSess] sendLoop encode message error ", err)
		return
	}
	if this._isInputMsg.Load() {
		this.logMsg(msg, msgBuf)
	}
	_, err = this.conn.Write(msgBuf)
	if err != nil {
		logger.Error("[NetSess] SendNow message  errno: ", common.GetErrno(err), " msg: ", err)
		this.Close()
		return
	}
}

// 开始
func (this *NetSess) Start() {
	if this.isClient {
		this.hbTicker = time.NewTicker(this.hbTickerV)
		this.timeout = &time.Timer{}
	} else {
		this.hbTicker = &time.Ticker{}
		this.timeout = time.NewTimer(this.timeoutV)
	}
	this.thgo.Go(this.sendLoop)
	this.thgo.Go(this.recvLoop)
}

/*
关闭 只会被运行一次 关闭的逻辑需要立即运行，处理链接对应的附加逻辑。
不然如果是多链接顶替逻辑的时候，有可能出现新链接来了然后被关了的情况。
因为新链接来的时候
*/
func (this *NetSess) Close() {
	if !this.isClose.CompareAndSwap(false, true) {
		//表示被运行过
		return
	}
	if this.callback != nil {
		this.callback.SessClosed(this)
	}
	if this.msgh != nil {
		msg := new(protomsg.SessClosed)
		msg.SessUID = this.GetID()
		_ = this.msgh.DoNormalMsg(msg.Name(), msg)
	}
	// 发起挂断session，不能使用Send
	this.sendBuf.Put(&protomsg.Hangup{})
	logger.Infof("Sess Close. keyid:%d", this.GetID())

}

/* 外部接口 */
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
